]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
St: support css margin property
authorCarlos Soriano <carlos.soriano89@gmail.com>
Tue, 11 Nov 2014 14:04:46 +0000 (15:04 +0100)
committerCarlos Soriano <carlos.soriano89@gmail.com>
Wed, 19 Nov 2014 15:42:25 +0000 (16:42 +0100)
It's implemented similar to the padding property, but instead of taking
into account the margin values at drawing time in node-drawing, we set
the clutter actor margins in StWidget when the style is computed.

In the case that a CSS margin is not specified, we don't to set a value
of 0 to the clutter actor margin. In this manner it allows to use
Clutter margin values set in the code. However, the margins that are set
both in the code and in the CSS on the same side, the result is
unpredictable.

We avoid to set the clutter actor margin values to 0 if there's no CSS
margin values defined, so we still allow clutter actors to use margin
set in the code.

https://bugzilla.gnome.org/show_bug.cgi?id=728437

src/st/st-theme-node-private.h
src/st/st-theme-node.c
src/st/st-theme-node.h
src/st/st-widget.c
src/st/test-theme.c
src/st/test-theme.css

index 1d7e9985f2ba9a9901c83cb14f12f7b8efb15ab8..7a2a383c0e3f70478d0dff517b6b8c28b9bc879d 100644 (file)
@@ -25,6 +25,7 @@
 #include <gdk/gdk.h>
 
 #include "st-theme-node.h"
+#include <libcroco/libcroco.h>
 #include "st-types.h"
 
 G_BEGIN_DECLS
@@ -58,6 +59,7 @@ struct _StThemeNode {
   int border_radius[4];
   int outline_width;
   guint padding[4];
+  guint margin[4];
 
   int width;
   int height;
@@ -90,6 +92,8 @@ struct _StThemeNode {
   guint background_position_set : 1;
   guint background_repeat : 1;
 
+  gboolean margin_set : 4;
+
   guint properties_computed : 1;
   guint geometry_computed : 1;
   guint background_computed : 1;
@@ -121,6 +125,8 @@ struct _StThemeNodeClass {
 
 void _st_theme_node_ensure_background (StThemeNode *node);
 void _st_theme_node_ensure_geometry (StThemeNode *node);
+void _st_theme_node_apply_margins (StThemeNode *node,
+                                   ClutterActor *actor);
 
 G_END_DECLS
 
index 661789e7ecbe52f59fe67ed921fd7fb65108cc4c..c6c6e8917377b87fe27fbfa1f54c3c40b966e80f 100644 (file)
@@ -1657,6 +1657,100 @@ do_padding_property (StThemeNode   *node,
     }
 }
 
+static void
+do_margin_property_term (StThemeNode *node,
+                         CRTerm      *term,
+                         gboolean     left,
+                         gboolean     right,
+                         gboolean     top,
+                         gboolean     bottom)
+{
+  int value;
+
+  if (get_length_from_term_int (node, term, FALSE, &value) != VALUE_FOUND)
+    return;
+
+  if (left)
+    {
+      node->margin[ST_SIDE_LEFT] = value;
+      node->margin_set |= 1 << ST_SIDE_LEFT;
+    }
+  if (right)
+    {
+      node->margin[ST_SIDE_RIGHT] = value;
+      node->margin_set |= 1 << ST_SIDE_RIGHT;
+    }
+  if (top)
+    {
+      node->margin[ST_SIDE_TOP] = value;
+      node->margin_set |= 1 << ST_SIDE_TOP;
+    }
+  if (bottom)
+    {
+      node->margin[ST_SIDE_BOTTOM] = value;
+      node->margin_set |= 1 << ST_SIDE_BOTTOM;
+    }
+}
+
+static void
+do_margin_property (StThemeNode   *node,
+                    CRDeclaration *decl)
+{
+  const char *property_name = decl->property->stryng->str + 6; /* Skip 'margin' */
+
+  if (strcmp (property_name, "") == 0)
+    {
+      /* Slight deviation ... if we don't understand some of the terms and understand others,
+       * then we set the ones we understand and ignore the others instead of ignoring the
+       * whole thing
+       */
+      if (decl->value == NULL) /* 0 values */
+        return;
+      else if (decl->value->next == NULL) /* 1 value */
+        {
+          do_margin_property_term (node, decl->value, TRUE, TRUE, TRUE, TRUE); /* left/right/top/bottom */
+          return;
+        }
+      else if (decl->value->next->next == NULL) /* 2 values */
+        {
+          do_margin_property_term (node, decl->value,       FALSE, FALSE, TRUE,  TRUE);  /* top/bottom */
+          do_margin_property_term (node, decl->value->next, TRUE, TRUE,   FALSE, FALSE); /* left/right */
+        }
+      else if (decl->value->next->next->next == NULL) /* 3 values */
+        {
+          do_margin_property_term (node, decl->value,             FALSE, FALSE, TRUE,  FALSE); /* top */
+          do_margin_property_term (node, decl->value->next,       TRUE,  TRUE,  FALSE, FALSE); /* left/right */
+          do_margin_property_term (node, decl->value->next->next, FALSE, FALSE, FALSE, TRUE);  /* bottom */
+        }
+      else if (decl->value->next->next->next->next == NULL) /* 4 values */
+        {
+          do_margin_property_term (node, decl->value,                   FALSE, FALSE, TRUE,  FALSE); /* top */
+          do_margin_property_term (node, decl->value->next,             FALSE, TRUE,  FALSE, FALSE); /* right */
+          do_margin_property_term (node, decl->value->next->next,       FALSE, FALSE, FALSE, TRUE);  /* bottom */
+          do_margin_property_term (node, decl->value->next->next->next, TRUE,  FALSE, FALSE, FALSE); /* left */
+        }
+      else
+        {
+          g_warning ("Too many values for margin property");
+          return;
+        }
+    }
+  else
+    {
+      if (decl->value == NULL || decl->value->next != NULL)
+        return;
+
+      if (strcmp (property_name, "-left") == 0)
+        do_margin_property_term (node, decl->value, TRUE,  FALSE, FALSE, FALSE);
+      else if (strcmp (property_name, "-right") == 0)
+        do_margin_property_term (node, decl->value, FALSE, TRUE,  FALSE, FALSE);
+      else if (strcmp (property_name, "-top") == 0)
+        do_margin_property_term (node, decl->value, FALSE, FALSE, TRUE,  FALSE);
+      else if (strcmp (property_name, "-bottom") == 0)
+        do_margin_property_term (node, decl->value, FALSE, FALSE, FALSE, TRUE);
+    }
+}
+
 static void
 do_size_property (StThemeNode   *node,
                   CRDeclaration *decl,
@@ -1707,6 +1801,8 @@ _st_theme_node_ensure_geometry (StThemeNode *node)
         do_outline_property (node, decl);
       else if (g_str_has_prefix (property_name, "padding"))
         do_padding_property (node, decl);
+      else if (g_str_has_prefix (property_name, "margin"))
+        do_margin_property (node, decl);
       else if (strcmp (property_name, "width") == 0)
         do_size_property (node, decl, &width);
       else if (strcmp (property_name, "height") == 0)
@@ -2257,6 +2353,18 @@ st_theme_node_get_padding (StThemeNode *node,
   return node->padding[side];
 }
 
+double
+st_theme_node_get_margin (StThemeNode *node,
+                          StSide side)
+{
+  g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
+  g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
+
+  _st_theme_node_ensure_geometry (node);
+
+  return node->margin[side];
+}
+
 /**
  * st_theme_node_get_transition_duration:
  * @node: an #StThemeNode
@@ -3029,6 +3137,28 @@ st_theme_node_get_vertical_padding (StThemeNode *node)
   return padding;
 }
 
+void
+_st_theme_node_apply_margins (StThemeNode *node,
+                              ClutterActor *actor)
+{
+  g_return_if_fail (ST_IS_THEME_NODE (node));
+
+  _st_theme_node_ensure_geometry (node);
+
+  // In the case that a CSS margin is not specified, we don't to set a value
+  // of 0 to the clutter actor margin. In this manner it allows to use Clutter
+  // margin values set in the code. However, the margins that are set both in
+  // the code and in the CSS on the same side, the result is unpredictable.
+  if (node->margin_set & 1 << ST_SIDE_LEFT)
+     clutter_actor_set_margin_left (actor, st_theme_node_get_margin(node, ST_SIDE_LEFT));
+  if (node->margin_set & 1 << ST_SIDE_RIGHT)
+     clutter_actor_set_margin_right (actor, st_theme_node_get_margin(node, ST_SIDE_RIGHT));
+  if (node->margin_set & 1 << ST_SIDE_TOP)
+     clutter_actor_set_margin_top (actor, st_theme_node_get_margin(node, ST_SIDE_TOP));
+  if (node->margin_set & 1 << ST_SIDE_BOTTOM)
+     clutter_actor_set_margin_bottom (actor, st_theme_node_get_margin(node, ST_SIDE_BOTTOM));
+}
+
 static GetFromTermResult
 parse_shadow_property (StThemeNode       *node,
                        CRDeclaration     *decl,
index dfe206ec06ba3fcade88aba60ed2233a8f9b5986..a193f5ae75ee297c140e1fbc797da15c543df545 100644 (file)
@@ -208,6 +208,9 @@ double st_theme_node_get_padding       (StThemeNode  *node,
 double st_theme_node_get_horizontal_padding (StThemeNode *node);
 double st_theme_node_get_vertical_padding   (StThemeNode *node);
 
+double st_theme_node_get_margin       (StThemeNode  *node,
+                                       StSide        side);
+
 int    st_theme_node_get_width         (StThemeNode  *node);
 int    st_theme_node_get_height        (StThemeNode  *node);
 int    st_theme_node_get_min_width     (StThemeNode  *node);
index c62b598c69e9196bb005c9cdb1d03177537cf360..de0ed958a763ffd7927df38ce6d0b9ae9db4fb08 100644 (file)
@@ -40,6 +40,7 @@
 #include "st-texture-cache.h"
 #include "st-theme-context.h"
 #include "st-theme-node-transition.h"
+#include "st-theme-node-private.h"
 
 #include "st-widget-accessible.h"
 
@@ -1540,6 +1541,8 @@ st_widget_recompute_style (StWidget    *widget,
       return;
     }
 
+  _st_theme_node_apply_margins (new_theme_node, CLUTTER_ACTOR (widget));
+
   if (!old_theme_node ||
       !st_theme_node_geometry_equal (old_theme_node, new_theme_node))
     clutter_actor_queue_relayout ((ClutterActor *) widget);
index 5fa5de3cbaeef082da6068398cc989303dd6c593..6c95c61d5ebc26135a03daea88d2f6fe96138da3 100644 (file)
@@ -34,6 +34,9 @@ static StThemeNode *group2;
 static StThemeNode *text3;
 static StThemeNode *text4;
 static StThemeNode *group3;
+static StThemeNode *group4;
+static StThemeNode *group5;
+static StThemeNode *group6;
 static StThemeNode *cairo_texture;
 static gboolean fail;
 
@@ -233,6 +236,19 @@ test_lengths (void)
   /* 1in == 72pt == 96px, at 96dpi */
   assert_length ("group1", "padding-left", 96.,
                 st_theme_node_get_padding (group1, ST_SIDE_LEFT));
+
+  /* 12pt == 16px at 96dpi */
+  assert_length ("group1", "margin-top", 16.,
+                st_theme_node_get_margin (group1, ST_SIDE_TOP));
+  /* 12px == 12px */
+  assert_length ("group1", "margin-right", 12.,
+                st_theme_node_get_margin (group1, ST_SIDE_RIGHT));
+  /* 2em == 32px (with a 12pt font) */
+  assert_length ("group1", "margin-bottom", 32.,
+                st_theme_node_get_margin (group1, ST_SIDE_BOTTOM));
+  /* 1in == 72pt == 96px, at 96dpi */
+  assert_length ("group1", "margin-left", 96.,
+                st_theme_node_get_margin (group1, ST_SIDE_LEFT));
 }
 
 static void
@@ -282,6 +298,54 @@ test_padding (void)
                 st_theme_node_get_padding (group2, ST_SIDE_LEFT));
 }
 
+static void
+test_margin (void)
+{
+  test = "margin";
+  /* Test that a 4-sided margin property assigns the right margin to
+   * all sides */
+  assert_length ("group2", "margin-top", 1.,
+                st_theme_node_get_margin (group2, ST_SIDE_TOP));
+  assert_length ("group2", "margin-right", 2.,
+                st_theme_node_get_margin (group2, ST_SIDE_RIGHT));
+  assert_length ("group2", "margin-bottom", 3.,
+                st_theme_node_get_margin (group2, ST_SIDE_BOTTOM));
+  assert_length ("group2", "margin-left", 4.,
+                st_theme_node_get_margin (group2, ST_SIDE_LEFT));
+
+  /* Test that a 3-sided margin property assigns the right margin to
+   * all sides */
+  assert_length ("group4", "margin-top", 1.,
+     st_theme_node_get_margin (group4, ST_SIDE_TOP));
+  assert_length ("group4", "margin-right", 2.,
+     st_theme_node_get_margin (group4, ST_SIDE_RIGHT));
+  assert_length ("group4", "margin-bottom", 3.,
+     st_theme_node_get_margin (group4, ST_SIDE_BOTTOM));
+  assert_length ("group4", "margin-left", 2.,
+     st_theme_node_get_margin (group4, ST_SIDE_LEFT));
+
+  /* Test that a 2-sided margin property assigns the right margin to
+   * all sides */
+  assert_length ("group5", "margin-top", 1.,
+     st_theme_node_get_margin (group5, ST_SIDE_TOP));
+  assert_length ("group5", "margin-right", 2.,
+     st_theme_node_get_margin (group5, ST_SIDE_RIGHT));
+  assert_length ("group5", "margin-bottom", 1.,
+     st_theme_node_get_margin (group5, ST_SIDE_BOTTOM));
+  assert_length ("group5", "margin-left", 2.,
+     st_theme_node_get_margin (group5, ST_SIDE_LEFT));
+
+  /* Test that all sides have a margin of 0 when not specified */
+  assert_length ("group6", "margin-top", 0.,
+     st_theme_node_get_margin (group6, ST_SIDE_TOP));
+  assert_length ("group6", "margin-right", 0.,
+     st_theme_node_get_margin (group6, ST_SIDE_RIGHT));
+  assert_length ("group6", "margin-bottom", 0.,
+     st_theme_node_get_margin (group6, ST_SIDE_BOTTOM));
+  assert_length ("group6", "margin-left", 0.,
+     st_theme_node_get_margin (group6, ST_SIDE_LEFT));
+}
+
 static void
 test_border (void)
 {
@@ -458,6 +522,12 @@ main (int argc, char **argv)
                               CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
   group2 = st_theme_node_new (context, root, NULL,
                               CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL);
+  group4 = st_theme_node_new (context, root, NULL,
+                              CLUTTER_TYPE_GROUP, "group4", NULL, NULL, NULL);
+  group5 = st_theme_node_new (context, root, NULL,
+                              CLUTTER_TYPE_GROUP, "group5", NULL, NULL, NULL);
+  group6 = st_theme_node_new (context, root, NULL,
+                              CLUTTER_TYPE_GROUP, "group6", NULL, NULL, NULL);
   text3 = st_theme_node_new  (context, group2, NULL,
                               CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
                               "color: #0000ff; padding-bottom: 12px;");
@@ -474,6 +544,7 @@ main (int argc, char **argv)
   test_type_inheritance ();
   test_adjacent_selector ();
   test_padding ();
+  test_margin ();
   test_border ();
   test_background ();
   test_font ();
@@ -484,6 +555,9 @@ main (int argc, char **argv)
   g_object_unref (group1);
   g_object_unref (group2);
   g_object_unref (group3);
+  g_object_unref (group4);
+  g_object_unref (group5);
+  g_object_unref (group6);
   g_object_unref (text1);
   g_object_unref (text2);
   g_object_unref (text3);
index 77ae34c1981c1541f2403943ec6c3a47d1bb9a7d..0c0af1b068b706211f66b0f3c7522d6879e88c42 100644 (file)
@@ -7,6 +7,11 @@ stage {
     padding-bottom: 2em;
     padding-left: 1in;
 
+    margin: 12pt;
+    margin-right: 12px;
+    margin-bottom: 2em;
+    margin-left: 1in;
+
     background: #ff0000 url('some-background.png');
 }
 
@@ -58,6 +63,7 @@ stage > #text2 {
 #group2 {
     background-image: url('other-background.png');
     padding: 1px 2px 3px 4px;
+    margin: 1px 2px 3px 4px;
 
     border: 2px solid #000000;
     border-bottom: 5px solid #0000ff;
@@ -75,3 +81,15 @@ ClutterText:visited, StLabel:visited {
 StLabel:boxed {
     border: 1px;
 }
+
+#group4 {
+    margin: 1px 2px 3px;
+}
+
+#group5 {
+    margin: 1px 2px;
+}
+
+#group6 {
+    padding: 5px;
+}